package com.baturu.mybatisminus.injector;

import com.baturu.mybatisminus.MinusConfiguration;
import com.baturu.mybatisminus.injector.parser.PartTreeSqlGenerator;
import com.baturu.mybatisminus.model.MethodInfo;
import com.baturu.mybatisminus.model.TableInfo;
import com.baturu.mybatisminus.util.ClassUtils;
import com.baturu.mybatisminus.util.EntityParseUtils;
import com.baturu.mybatisminus.util.MethodParseUtils;
import com.baturu.mybatisminus.util.ReflectionUtils;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 默认的BaseDAO方法名规范 SQL注入类，目前仅支持mysql
 *
 * @author huangbin
 **/
public class DefaultMethodNameSqlInjector implements MethodNameSqlInjector {

    private static final Log logger = LogFactory.getLog(DefaultMethodNameSqlInjector.class);

    protected MinusConfiguration configuration;
    protected LanguageDriver languageDriver;
    protected MapperBuilderAssistant builderAssistant;

    /**
     * 注入根据方法名生成的SQL
     */
    @Override
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        this.configuration = (MinusConfiguration) builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        Class<?> entityClass = ReflectionUtils.extractEntityClass(mapperClass);
        Class<?> idClass = ReflectionUtils.extractIdClass(mapperClass);
        TableInfo tableInfo = EntityParseUtils.parseEntityClass(builderAssistant, entityClass, idClass);
        List<Method> methodList = ClassUtils.getCustomerMethod(mapperClass);
        for (Method method : methodList) {
            if (hasMappedStatement(tableInfo.getStatementId(method.getName()))) {
                continue;
            }
            MethodInfo methodInfo = MethodParseUtils.parseMethod(mapperClass, method);
            PartTreeSqlGenerator partTreeSqlGenerator = new PartTreeSqlGenerator(methodInfo, tableInfo);
            addMappedStatement(partTreeSqlGenerator, methodInfo, tableInfo);
        }
    }


    private void addMappedStatement(PartTreeSqlGenerator partTreeSqlGenerator, MethodInfo methodInfo, TableInfo tableInfo) {
        String sql = partTreeSqlGenerator.generateSql();
        String statementId = getStatementId(methodInfo, tableInfo);
        showSql(statementId, sql);
        SqlCommandType sqlCommandType = partTreeSqlGenerator.getSqlCommandType();

        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, methodInfo.getParameterType());

        boolean isSelect = false;
        if (sqlCommandType == SqlCommandType.SELECT) {
            isSelect = true;
        }
        builderAssistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null,
                methodInfo.getParameterType(), null, methodInfo.getResultType(), null, !isSelect, isSelect, false,
                new NoKeyGenerator(), null, null, configuration.getDatabaseId(), languageDriver, null);
    }


    private boolean hasMappedStatement(String mappedStatement) {
        return configuration.hasStatement(mappedStatement, false);
    }


    private String getStatementId(MethodInfo methodInfo, TableInfo tableInfo) {
        return tableInfo.getStatementId(methodInfo.getMethodName());
    }


    private void showSql(String statementId, String sql) {
        if (logger.isDebugEnabled() || configuration.isShowGeneratedSql()) {
            logger.debug(statementId + " 生成 sql:\n" + sql);
        }
    }


}
